import Designer from '@/index';
import Node from './node';
import Stage from './stage';
import { createLinearGradient, createRadialGradient, isNumber, midpoint, precision, toPx } from '@/utils';

/**
 * 文本类
 */
export default class Text extends Node {
  className = 'Text'; // 类名
  rows = []; // 文本行
  constructor(config = {}) {
    super(config);
    this.text = config.text ?? '';
    this.color = config.color ?? 'black';
    this.strokeColor = config.strokeColor ?? 'transparent';
    this.strokeWidth = config.strokeWidth ?? 1;
    this.strokeJoin = config.strokeJoin ?? 'round';
    // normal | italic | oblique
    this.fontStyle = config.fontStyle ?? 'normal';
    // normal | small-caps
    this.fontVariant = config.fontVariant ?? 'normal';
    // normal | bold | bolder | lighter | 100 | 200 | ...
    this.fontWeight = config.fontWeight ?? 'normal';
    this.fontSize = config.fontSize ?? 12;
    this.fontFamily = config.fontFamily ?? Designer.defaultFontFamily;
    // 水平对齐方式 left | center | right
    this.align = config.align ?? 'left';
    // 垂直对齐方式  top | middle | bottom
    this.verticalAlign = config.verticalAlign ?? 'middle';
    // 字符间距
    this.letterSpacing = config.letterSpacing ?? 0;
    // 行高 默认1倍
    this.lineHeight = config.lineHeight ?? this.fontSize * 1;
    // 删除线
    this.lineThrough = config.lineThrough ?? false;
    // 下划线
    this.underline = config.underline ?? false;
    // 线性渐变
    this.linearGradient = config.linearGradient ?? null;
    // 径向渐变
    this.radialGradient = config.radialGradient ?? null;
    // 描边线性渐变
    this.strokeLinearGradient = config.strokeLinearGradient ?? null;
    // 描边径向渐变
    this.strokeRadialGradient = config.strokeRadialGradient ?? null;
    // 文本自动宽度
    this.autoWidth = config.autoWidth ?? true;
    // 更新文本
    this.update();
    // 设置锚点集
    this.setAnchors(config.anchors ?? false, true);
  }
  // 默认锚点集
  get defaultAnchors() {
    const { width, height } = this;
    if (!width || !height) return [];
    const radius = 4;
    return [
      { id: 'top', name: 'top', x: width / 2, y: 0 - radius, radius },
      { id: 'right', name: 'right', x: width + radius, y: height / 2, radius },
      { id: 'bottom', name: 'bottom', x: width / 2, y: height + radius, radius },
      { id: 'left', name: 'left', x: 0 - radius, y: height / 2, radius },
    ];
  }
  // 更新文本
  update() {
    const { rows, lineHeight } = this;
    const ctx = Stage.canvas.getContext();
    ctx.save();
    this.updateCtx(ctx);
    // 最大宽度 有设置宽度使用设置的宽度，否则自动
    const maxWidth = isNumber(this.width) && this.width > 0 ? this.width : 'auto';
    // 更新文本行
    rows.splice(0, rows.length, ...Text.textRows(this, maxWidth));
    // 最大宽度自动时 更新文本区域宽度为 宽度最大的行
    if (maxWidth === 'auto') this.width = Math.max(0.001, ...rows.map((i) => measureText(ctx, i)));
    // 更新文本区域高度
    this.height = rows.length * lineHeight;
    ctx.restore();
  }
  // 同步文本样式
  syncStyle(el) {
    const {
      text,
      fontStyle,
      fontVariant,
      fontWeight,
      fontSize,
      fontFamily,
      align,
      letterSpacing,
      lineHeight,
      lineThrough,
      underline,
    } = this;
    const { style } = el;
    el.value = text;
    style.fontStyle = fontStyle;
    style.fontVariant = fontVariant;
    style.fontWeight = fontWeight;
    style.fontSize = toPx(fontSize);
    style.fontFamily = fontFamily;
    style.textAlign = align;
    style.letterSpacing = toPx(letterSpacing);
    style.lineHeight = toPx(lineHeight);
    style.textDecoration = '';
    style.textDecoration = `${underline ? 'underline' : ''} ${lineThrough ? 'line-through' : ''}`;
  }
  // 更新 ctx
  updateCtx(ctx) {
    const { fontStyle, fontVariant, fontWeight, fontSize, fontFamily, align, letterSpacing } = this;
    ctx.textBaseline = 'middle'; // 文本基线定死为 em 方框的正中
    ctx.textAlign = align;
    ctx.font = `${fontStyle} ${fontVariant} ${fontWeight} ${toPx(fontSize)} ${fontFamily}`;
    ctx.letterSpacing = toPx(letterSpacing);
  }
  // 绘制主要内容
  drawMain(ctx) {
    const {
      width,
      rows,
      color,
      strokeColor,
      strokeWidth,
      strokeJoin,
      fontSize,
      fontWeight,
      lineHeight,
      align,
      verticalAlign,
      lineThrough,
      underline,
    } = this;
    this.updateCtx(ctx);
    // em 方框与行高之间的空白间隙
    const interval = (lineHeight - fontSize) / 2;
    // 文本绘制 x 坐标偏移量：水平对齐方式对应的间隙偏移
    const offsetX = { center: width / 2, right: width }[align] || 0;
    // 文本绘制 y 坐标偏移量：行高一半（文本基线middle） + 垂直对齐方式对应的间隙偏移
    const offsetY = lineHeight / 2 + ({ top: -interval, bottom: interval }[verticalAlign] || 0);
    // 文本修饰线宽 粗体 ? 1 : 0.5
    const dlw = (['bold', 'bolder', '600', '700', '800', '900'].includes(fontWeight) ? 1 : 0.5) * (fontSize / 12);
    // 线性渐变
    const linearGradient = createLinearGradient(ctx, this, this.linearGradient);
    // 径向渐变
    const radialGradient = createRadialGradient(ctx, this, this.radialGradient);
    // 描边线性渐变
    const strokeLinearGradient = createLinearGradient(ctx, this, this.strokeLinearGradient);
    // 描边径向渐变
    const strokeRadialGradient = createRadialGradient(ctx, this, this.strokeRadialGradient);
    // 逐行绘制文本
    rows.forEach((row, i) => {
      const finalX = offsetX;
      const finalY = i * lineHeight + offsetY;
      const finalWidth = measureText(ctx, row);
      const lineStartX = { center: (width - finalWidth) / 2, right: width - finalWidth }[align] || 0;
      const lineEndX = lineStartX + finalWidth;
      // 设置文本填充颜色 及 描边宽度、颜色、拐角样式
      ctx.fillStyle = linearGradient || radialGradient || color;
      ctx.strokeStyle = strokeLinearGradient || strokeRadialGradient || strokeColor;
      ctx.lineWidth = strokeWidth * 2; // 描边线宽 * 2（描边中心为文本轮廓线，因此实际展示线宽为一半，需要*2）
      ctx.lineJoin = strokeJoin;
      // 绘制单行文本
      ctx.strokeText(row, finalX, finalY);
      ctx.fillText(row, finalX, finalY);
      // 设置文本修饰线宽及颜色
      ctx.lineWidth = dlw;
      ctx.strokeStyle = linearGradient || radialGradient || color;
      // 删除线
      if (lineThrough) {
        ctx.beginPath();
        ctx.moveTo(lineStartX, finalY);
        ctx.lineTo(lineEndX, finalY);
        ctx.stroke();
      }
      // 下划线
      if (underline) {
        ctx.beginPath();
        ctx.moveTo(lineStartX, finalY + fontSize / 2 - dlw * 0.5);
        ctx.lineTo(lineEndX, finalY + fontSize / 2 - dlw * 0.5);
        ctx.stroke();
      }
    });
  }
  /**
   * Methods
   */
  /**
   * 设置文本
   * @param {string} text
   */
  setText(text, isRecord = true) {
    this.text = text;
    const { align, autoWidth, realRect, stage } = this;
    const { points } = realRect;
    const fixedPoint = { left: points[0], right: points[1], center: midpoint(points[0], points[1]) }[align];
    // 文本自动宽度 设置宽度为文本最大宽度
    if (autoWidth) this.width = Text.measureMaxWidth(this);
    this.update();
    const { points: nPoints } = this.realRect;
    const nFixedPoint = { left: nPoints[0], right: nPoints[1], center: midpoint(nPoints[0], nPoints[1]) }[align];
    const offset = { x: fixedPoint.x - nFixedPoint.x, y: fixedPoint.y - nFixedPoint.y };
    (this.x += offset.x), (this.y += offset.y); // 位置修正
    this.updateParent();
    if (!stage) return;
    stage.emit('setText', this);
    stage.transformer.update(this);
    // 重绘
    stage.draw();
    if (isRecord) stage.handleRecord();
  }
  /**
   * 配置更新
   * @param {*} config
   */
  setConfig(config) {
    Object.assign(this, config);
    // 会影响宽度的配置属性
    const widthEffectMap = {
      text: 1,
      fontStyle: 1,
      fontVariant: 1,
      fontWeight: 1,
      fontSize: 1,
      fontFamily: 1,
      letterSpacing: 1,
    };
    // 判断配置是否会影响宽度
    const widthEffect = Object.keys(config).some((key) => widthEffectMap[key]);
    // 配置会影响宽度 且 文本自动宽度 设置宽度为文本最大宽度
    if (widthEffect && this.autoWidth) this.width = Text.measureMaxWidth(this);
    this.update();
    this.updateParent();
    this.stage?.transformer.update(this);
    this.stage?.textareaEl.update();
    // 重绘
    this.stage?.draw();
    this.stage?.handleRecord();
  }
  /**
   * Static Methods
   */
  /**
   * 测量文本最大宽度
   * @param {*} config
   */
  static measureMaxWidth(config) {
    const {
      text = '',
      fontStyle = 'normal',
      fontVariant = 'normal',
      fontWeight = 'normal',
      fontSize = 12,
      fontFamily = Designer.defaultFontFamily,
      letterSpacing,
    } = config;
    const ctx = Stage.canvas.getContext();
    ctx.save();
    ctx.textBaseline = 'middle'; // 文本基线定死为 em 方框的正中
    ctx.font = `${fontStyle} ${fontVariant} ${fontWeight} ${toPx(fontSize)} ${fontFamily}`;
    // @ts-ignore
    ctx.letterSpacing = toPx(letterSpacing);
    const rows = [];
    let temp = '';
    for (const char of text.split('')) {
      temp += char;
      // 换行符直接换行
      if (char === '\n' || char === '\r') {
        rows.push(temp.slice(0, -1));
        temp = '';
      }
    }
    if (temp) rows.push(temp);
    const maxWidth = Math.max(0, ...rows.map((i) => measureText(ctx, i)));
    ctx.restore();
    return maxWidth;
  }
  /**
   * 获取文本行
   * @param {*} config
   * @param {number | 'auto'} maxWidth 文本最大宽度
   */
  static textRows(config, maxWidth = 'auto') {
    const {
      text = '',
      fontStyle = 'normal',
      fontVariant = 'normal',
      fontWeight = 'normal',
      fontSize = 12,
      fontFamily = Designer.defaultFontFamily,
      letterSpacing,
    } = config;
    const ctx = Stage.canvas.getContext();
    ctx.save();
    ctx.textBaseline = 'middle'; // 文本基线定死为 em 方框的正中
    ctx.font = `${fontStyle} ${fontVariant} ${fontWeight} ${toPx(fontSize)} ${fontFamily}`;
    // @ts-ignore
    ctx.letterSpacing = toPx(letterSpacing);
    const rows = [];
    let temp = '';
    for (const char of text.split('')) {
      temp += char;
      // 换行符直接换行
      if (char === '\n' || char === '\r') {
        rows.push(temp.slice(0, -1));
        temp = '';
        continue;
      }
      // @ts-ignore 文本长度大于最大宽度时换行
      if (isNumber(maxWidth) && measureText(ctx, temp) > maxWidth) {
        temp.length > 1 && rows.push(temp.slice(0, -1));
        temp = temp.slice(-1);
      }
    }
    rows.push(temp);
    ctx.restore();
    return rows;
  }
}

// 测量文本宽度 https://developer.mozilla.org/zh-CN/docs/Web/API/TextMetrics#%E6%B5%8B%E9%87%8F%E6%96%87%E6%9C%AC%E5%AE%BD%E5%BA%A6
const measureText = (ctx, text) => {
  const textMetrics = ctx.measureText(text);
  return precision(textMetrics.width);
  return precision(textMetrics.actualBoundingBoxRight + textMetrics.actualBoundingBoxLeft);
};

// fontFamily 补充默认字体
const fontFamilyFilter = (fontFamily) => {
  if (!fontFamily) return Designer.defaultFontFamily;
  return `${fontFamily},${Designer.defaultFontFamily}`;
};
